package org.red5.demo;


import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.opencv.video.Video;
import org.red5.Repository.VideoRepository;
import org.red5.daemon.TVideoPara;
import org.red5.entity.Image;
import org.red5.websocket.WebsocketAPI;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.List;

import static org.red5.demo.ParameterAPI.sVideoRepository;
import static org.red5.demo.ParameterAPI.sWebsocketAPI;

/**
 * 采用javacv ffmpeg 方式启动视频流 比javacv启动稳定丢包率低
 */
@Component
@Configuration
public class PushRTMP {

    static boolean exit = false;
    public static String videoFormat;
    public static boolean pushWebSocket;
    /**
     * upd rtsp视频初始化调用接口
     */
    public static void iniVideo() {
        List<TVideoPara> videoList = ParameterAPI.sVideoRepository.findAll();
        for (TVideoPara videoPara : videoList) {
                String ip = videoPara.getLanIpaddr();
                String name = videoPara.getLoginName();
                String passworld = videoPara.getLoginPassworld();
                String type = videoPara.getDataRateType();
                String vender = videoPara.getVender();

                new Thread(new Runnable() {
                    public void run() {
                        try {
                            if("1".equals(videoFormat)) {
                                runUDP(ip, name, passworld, type,vender);
                            }else if("2".equals(videoFormat)){
                                runTCP(ip, name, passworld, type,vender);
                            }else if("3".equals(videoFormat)) {
                                runCMD(ip, name, passworld, type,vender);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
        }
       // iniVideoCMDTest();
    }


    /**
     * ffmpeg cmd命令方式视频拉流测试接口
     */
    public static void iniVideoCMDTest() {
        new Thread(new Runnable() {
            public void run() {
                try {
                    runTestTCP("192.168.20.12", "admin", "12345", "1");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                try {
                    runTestTCP("192.168.20.13", "admin", "12345", "1");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
    /**
     * tcp 视频拉推流调用接口
     */
    public static void runTCP(String ip, String username, String password, String rateType,String vender) {

        if (ParameterAPI.VideoMap.size() > 0 && ParameterAPI.VideoMap.get(ip) != null && ParameterAPI.VideoMap.get(ip)) {
            return;
        } else if (ParameterAPI.VideoMap.size() == 0 || ParameterAPI.VideoMap.get(ip) == null) {

            ParameterAPI.VideoMap.put(ip, true);
            System.out.println("start...");
            String rtspPath = "";  //直接拉取网络rtmp 流
            if(vender.equals("TPLink")) {
                 rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/" + (("1".equals(rateType)) ? "stream1" : ("2".equals(rateType) ? "stream2" : "stream3")); //拉取本地 摄像机视频流
            }else{
                rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/h264/ch1/" + (("1".equals(rateType)) ? "main" : ("2".equals(rateType) ? "sub" : "stream3")) + "/av_stream"; //拉取本地 摄像机视频流
            }
             //String rtspPath = "rtmps://localhost:8443/oflaDemo/sssssss"; //oflaDemo    //转换流地址 访问使用rtmps://localhost:8443/oflaDemo/sssssss
            String rtmpPath = "rtmp://localhost:1935/oflaDemo/" + ip;

            int audioRecord = 0; // 0 = 不录制，1=录制 音频是否录制
            boolean saveVideo = false;
            pushTCP(rtspPath, rtmpPath, audioRecord, saveVideo,ip);
            if (ParameterAPI.VideoMap.get(ip)) {
                ParameterAPI.VideoMap.remove(ip);
            }
            System.out.println("end...");
        }
    }

    /**
     * udp 视频拉推流调用接口
     */
    public static void runUDP(String ip, String username, String password, String rateType,String vender) {

        System.out.println("start...");
        //String rtmpPath = "rtmp://58.200.131.2:1935/livetv/gxtv";  //直接拉取网络rtmp 流
        String rtspPath = "";  //直接拉取网络rtmp 流
        if(vender.equals("TPLink")) {
            rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/" + (("1".equals(rateType)) ? "stream1" : ("2".equals(rateType) ? "stream2" : "stream3")); //拉取本地 摄像机视频流
        }else{
            rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/h264/ch1/" + (("1".equals(rateType)) ? "main" : ("2".equals(rateType) ? "sub" : "stream3")) + "/av_stream"; //拉取本地 摄像机视频流
        }
        //String rtspPath = "rtmps://localhost:8443/oflaDemo/sssssss"; //oflaDemo    //转换流地址 访问使用rtmps://localhost:8443/oflaDemo/sssssss
        String  rtmpPath= "rtmp://localhost:1935/oflaDemo/" + ip;

        int audioRecord = 0; // 0 = 不录制，1=录制 音频是否录制
        boolean saveVideo = false;

        try {
            pushUDP(rtspPath, rtmpPath, audioRecord, saveVideo,ip);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("end...");
    }

    /**
     * ffmepg cmd 视频拉推流调用接口
     */
    public static void runCMD(String ip, String username, String password, String rateType, String vender) {

        System.out.println("start...");
        String rtspPath = "";  //直接拉取网络rtmp 流
        if(vender.equals("TPLink")) {
            rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/" + (("1".equals(rateType)) ? "stream1" : ("2".equals(rateType) ? "stream2" : "stream3")); //拉取本地 摄像机视频流
        }else{
            rtspPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/h264/ch1/" + (("1".equals(rateType)) ? "main" : ("2".equals(rateType) ? "sub" : "stream3")) + "/av_stream"; //拉取本地 摄像机视频流
        }
        String rtmpPath = "rtmp://localhost:1935/oflaDemo/" + ip;
        try {
            ParameterAPI.VideoMap.put(ip, true);

            //sVideoRepository.updateUrlByIp(geturl(rtmpPath),ip);
            ThumbnailTool.testFFmpegRtsp(rtspPath,rtmpPath,1024,768);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //sVideoRepository.updateUrlByIp("",ip);
        ParameterAPI.VideoMap.remove(ip);
        System.out.println("end...");
    }

    public static void runTestTCP(String ip, String username, String password, String rateType) {

        if (ParameterAPI.VideoMap.size() > 0 && ParameterAPI.VideoMap.get(ip) != null && ParameterAPI.VideoMap.get(ip)) {
            return;
        } else if (ParameterAPI.VideoMap.size() == 0 || ParameterAPI.VideoMap.get(ip) == null) {

            ParameterAPI.VideoMap.put(ip, true);
            System.out.println("start...");
            //String rtmpPath = "rtmp://58.200.131.2:1935/livetv/gxtv";  //直接拉取网络rtmp 流
            String rtmpPath = "rtsp://" + username + ":" + password + "@" + ip + ":554/h264/ch1/" + (("1".equals(rateType)) ? "main" : ("2".equals(rateType) ? "sub" : "stream3")) + "/av_stream"; //拉取本地 摄像机视频流

            //String rtspPath = "rtmps://localhost:8443/oflaDemo/sssssss"; //oflaDemo    //转换流地址 访问使用rtmps://localhost:8443/oflaDemo/sssssss
             String rtspPath = "rtmp://localhost:1935/oflaDemo/" + ip;
            //String rtspPath = "http://127.0.0.1/oflaDemo/sssssss/playlist.m3u8";

            int audioRecord = 0; // 0 = 不录制，1=录制 音频是否录制
            boolean saveVideo = false;
            pushTCP(rtmpPath, rtspPath, audioRecord, saveVideo,ip);
            if (ParameterAPI.VideoMap.get(ip)) {
                ParameterAPI.VideoMap.remove(ip);
            }
            System.out.println("end...");
        }
    }

    /**
     * tcp 视拉频推流接口
     */
    @SuppressWarnings("resource")
    private static void pushTCP(String rtmpPath, String rtspPath, int audioRecord, boolean saveVideo,String ip) {
        // 使用rtsp的时候需要使用 FFmpegFrameGrabber，不能再用 FrameGrabber
        Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
        int width = 600, height = 480;
        try {
            FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(rtmpPath);
            grabber.setOption("rtsp_transport", "tcp"); // 使用tcp的方式，不然会丢包很严重

            grabber.setImageWidth(width);
            grabber.setImageHeight(height);
            System.out.println("grabber start");
            grabber.start();
            // 流媒体输出地址，分辨率（长，高），是否录制音频（0:不录制/1:录制）
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtspPath, width, height, audioRecord);
            recorder.setInterleaved(true);
            // recorder.setVideoOption("crf","28");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 28
            recorder.setFormat("flv"); // rtmp的类型 3gp avi mp4 flv

            recorder.setFrameRate(24);
            recorder.setImageWidth(width);
            recorder.setImageHeight(height);
            recorder.setPixelFormat(0); // yuv420p
            System.out.println("recorder start");
            recorder.start();

            System.out.println("all start!!");
            sVideoRepository.updateUrlByIp(geturl(rtspPath),ip);
            while (!exit) {
                Frame frame = grabber.grab();
                if (frame == null) {
                    System.out.println("grabber start");
                    grabber.start();

                    continue;
                }
                if(pushWebSocket) {
                    BufferedImage bufferedImage = java2DFrameConverter.getBufferedImage(frame);
                    byte[] bytes = imageToBytes(bufferedImage, "jpg");
                    sWebsocketAPI.sendAllByObject(1, new Image(bytes));
                }
                recorder.record(frame);
            }
            sVideoRepository.updateUrlByIp("",ip);
            grabber.stop();
            grabber.release();
            recorder.stop();
            recorder.release();
        } catch (Exception e) {
            //不抛异常
        }
    }


    /**
     * upd 视频拉推流接口
     */
    @SuppressWarnings("resource")
    private static void pushUDP(String rtmpPath, String rtspPath, int audioRecord, boolean saveVideo,String ip) {
        // 使用rtsp的时候需要使用 FFmpegFrameGrabber，不能再用 FrameGrabber
        Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
        int width = 300, height = 200;
        try {
            FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(rtmpPath);
            // grabber.setOption("rtsp_transport" , "tcp"); // 使用tcp的方式，不然会丢包很严重

            grabber.setImageWidth(width);
            grabber.setImageHeight(height);
            System.out.println("grabber start");
            grabber.start();
            // 流媒体输出地址，分辨率（长，高），是否录制音频（0:不录制/1:录制）
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtspPath, width, height, audioRecord);
            recorder.setInterleaved(true);
            // recorder.setVideoOption("crf","28");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 28
            recorder.setFormat("flv"); // rtmp的类型 3gp avi mp4 flv

            recorder.setFrameRate(15);
            recorder.setImageWidth(width);
            recorder.setImageHeight(height);
            recorder.setPixelFormat(0); // yuv420p
            System.out.println("recorder start");
            recorder.start();

            System.out.println("all start!!");
            sVideoRepository.updateUrlByIp(geturl(rtspPath),ip);
            while (!exit) {
                Frame frame = grabber.grab();
                if (frame == null) {
                    System.out.println("grabber start");
                    grabber.start();
                    continue;
                }
                if(pushWebSocket) {
                    BufferedImage bufferedImage = java2DFrameConverter.getBufferedImage(frame);
                    byte[] bytes = imageToBytes(bufferedImage, "jpg");
                    sWebsocketAPI.sendAllByObject(1, new Image(bytes));
                }
                recorder.record(frame);
            }
            sVideoRepository.updateUrlByIp("",ip);
            grabber.stop();
            grabber.release();
            recorder.stop();
            recorder.release();
        } catch (Exception e) {
            //不抛异常
        }
    }

    /**
     * 图片转字节数组
     *
     * @param bImage 图片数据
     * @param format 格式
     * @return 图片字节码
     */
    private static byte[] imageToBytes(BufferedImage bImage, String format) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(bImage, format, out);
        } catch (IOException e) {
            System.out.println("bufferImage 转 byte 数组异常");
            System.out.println("bufferImage transfer byte[] exception: " + e);
            return null;
        }
        return out.toByteArray();
    }



   public static String geturl(String rtspPath){
        InetAddress ia=null;
            try {
                 ia=ia.getLocalHost();
                 String localip=ia.getHostAddress();
                 System.out.println("本机的ip是 ："+localip);
                rtspPath= rtspPath.replace("localhost",localip);
             } catch (Exception e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
            return rtspPath;
    }

    @Value("${videoFormat}")
    public  void setVideoFormat(String videoFormat) {
        PushRTMP.videoFormat = videoFormat;
    }
    @Value("${pushWebSocket}")
    public static void setPushWebSocket(boolean pushWebSocket) {
        PushRTMP.pushWebSocket = pushWebSocket;
    }
}